package com.example.qxfw.jtyb.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.qxfw.common.entity.qx.MsgMediumSmallScale;
import com.example.qxfw.common.entity.qx.SmallScaleStation;
import com.example.qxfw.common.entity.zy.BaseProduct;
import com.example.qxfw.common.entity.zy.Traffic;
import com.example.qxfw.common.entity.zy.TrafficFcst;
import com.example.qxfw.common.entity.zy.TrafficWeatherSta;
import com.example.qxfw.common.util.DateUtil;
import com.example.qxfw.common.util.DocxUtil;
import com.example.qxfw.common.util.WeatherInfoUtil;
import com.example.qxfw.jtyb.entity.RelatedSites;
import com.example.qxfw.jtyb.entity.StationInformation;
import com.example.qxfw.jtyb.mapper.TrafficMapper;
import com.example.qxfw.jtyb.service.JtybService;
import com.example.qxfw.mongodb.hy.entity.HyDocFile;
import com.example.qxfw.mongodb.hy.service.DocFileService;
import com.example.qxfw.rcyw.mapper.SmallScaleStationMapper;
import com.example.qxfw.ybzz.mapper.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.File;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class JtybServiceImpl implements JtybService {
    @Value("${filePath.docFilePath}")
    private String docFilePath;
    @Value("${filePath.pdfFilePath}")
    private String pdfFilePath;
    @Value("${filePath.ip}")
    private String ip;
    @Value("${filePath.port}")
    private String port;
    @Autowired
    private TrafficWeatherStaMapper trafficWeatherStaMapper;
    @Autowired
    private TrafficMapper trafficMapper;
    @Autowired
    private MsgMediumSmallScaleMapper msgMediumSmallScaleMapper;
    @Autowired
    private TrafficFcstMapper trafficFcstMapper;
    @Autowired
    private SmallScaleStationMapper smallScaleStationMapper;
    @Autowired
    private DocFileService docFileService;
    @Autowired
    private BaseProductMapper baseProductMapper;

    @Override
    public String generateDocx(Traffic traffic) {
        String originalDate = traffic.getAnnouncedtime().toString();
        String originalFormat = "EEE MMM dd HH:mm:ss z yyyy";
        DateTimeFormatter originalFormatter = DateTimeFormatter.ofPattern(originalFormat, Locale.US);
        LocalDateTime localDateTime = LocalDateTime.parse(originalDate, originalFormatter);
        ZoneId cstTimeZone = ZoneId.of("Asia/Shanghai");
        ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, cstTimeZone);
        DateTimeFormatter targetFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String date = zonedDateTime.format(targetFormatter);
        String[] s = date.split(" ");
        String[] split = s[0].split("-");
        String ymd = new StringBuilder().append(split[0]).append("年").append(split[1]).append("月").append(split[2]).append("日").toString();

        //这是模板的文件位置
        String sourcePath = "templates/jtybTemp/交通预报模板.docx";
        //这是根据模板生成的文档的文件位置
        String docxPath = docFilePath;
        // 替换文档中的占位符并保留原始样式和格式
        String[] placeholders = {"【0】", "【1】", "【2】", "【3】", "【4】"};// 模板文件需要替换的位置
        int length = placeholders.length;
         String[] replacements = new String[length];
        // 想要替换的值
        replacements[0] = split[0];
        replacements[1] = "1";
        replacements[2] = "admin";
        replacements[3] = "玉林市气象台发布"+ymd+"交通气象等级预报";
        replacements[4] = ymd;
        // 调用生成docx文件的方法
        DocxUtil.generateWordDocx(sourcePath, docxPath, placeholders, replacements);
        // 保存到base_product中
        BaseProduct baseProduct = new BaseProduct();
        baseProduct.setProductname(ymd+"交通气象等级预报产品.docx");
        baseProduct.setProducttype("交通预报");
        baseProduct.setForecasttime(Timestamp.valueOf(date));
        baseProduct.setMaketime(DateUtil.localDateTimeToTimestamp(LocalDateTime.now()));
        baseProduct.setMaker("admin");
        baseProduct.setBindid(null);
        baseProduct.setContext(null);
        baseProduct.setState(null);
        int insert = baseProductMapper.insert(baseProduct);
        System.err.println(baseProduct);
        log.warn("baseProduct表中插入："+insert+"条数据完成");
        String url = "";
        if (insert>0) {
            // 只有文档生成后才能保存，否则无法保存
            File docxFile = new File(docxPath);
            if (docxFile.exists()) {
                //文件保存入数据库
                HyDocFile hyDocFile = new HyDocFile();
                try {
                    byte[] bytes = DocxUtil.readDocxFileToByteArray(docxPath);
                    hyDocFile.setData(bytes);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                hyDocFile.setId(String.valueOf(baseProduct.getId()));
                hyDocFile.setTable_name("base_product");
                hyDocFile.setName(baseProduct.getProducttype() + "docx");
                docFileService.save(hyDocFile);
                //生成pdf文档并返回url
                String name = ymd + "交通气象等级预报产品.pdf";
                String pdfPath = pdfFilePath + name;
                DocxUtil.wordToPdf(docxPath, pdfPath);
                url = "http://" + ip + ":" + port + "/temp/" + name;
            } else {
                // 文件生成失败，可以抛出异常或者执行其他处理逻辑
                throw new RuntimeException("生成docx文件失败！");
            }
        }
        return url;
    }


    /**
     *
     * @param date 日期
     * @param forecastType 预报类型
     * @param timeChar 时次（08、20）
     * @return
     */
    @Override
    public List<Traffic> forecastRelease(@Param("date") String date, @Param("forecastType") int forecastType, @Param("timeChar") String timeChar) {
        forcast(date, forecastType, timeChar);
        String sb = new StringBuilder().append(date).append(" ").append(timeChar).append(":00:00").toString();
        return trafficMapper.selectList(new LambdaQueryWrapper<Traffic>()
        .eq(Traffic::getAnnouncedtime, Timestamp.valueOf(sb))
        .eq(Traffic::getForecasttype, forecastType));
    }

    private List<Traffic> forcast(String dateAnnounce, int forecastType, String strHour) {
        boolean bSuccess = false;
        List<Traffic> listInsert = new ArrayList<>();
        int iInterval = forecastType;
        int iNTimes = 24;
        if (forecastType == 12) {
            iInterval = forecastType * 2;
            iNTimes = 168;
        }
        List<TrafficWeatherSta> list = trafficWeatherStaMapper.selectList(new LambdaQueryWrapper<TrafficWeatherSta>()
                .select(TrafficWeatherSta::getStationid, TrafficWeatherSta::getStationname, TrafficWeatherSta::getLongitude, TrafficWeatherSta::getLatitude));
        if (list.size() <= 0 || list == null) {
            log.warn("TrafficWeatherSta表中未查询到数据");
        }
        if (!strHour.isEmpty() || strHour != null) {
            dateAnnounce = (dateAnnounce + " " + strHour + ":00:00");
        }

        List<Traffic> insertListData = new ArrayList<>();
        List<TrafficFcst> listYB = trafficFcstMapper.selectBydateAndTimeAndForecastType(Timestamp.valueOf(dateAnnounce), strHour, forecastType);

        if (listYB != null && listYB.size() > 0) {
            loadTrafficStation();
            HashMap<String, String> tfStatoNaSta = getNerNatSta();
            String strNearSta = "";
            if (tfStatoNaSta != null && !tfStatoNaSta.isEmpty()) {
                StringBuilder strNearStaBuilder = new StringBuilder();

                for (String strvalue : tfStatoNaSta.values()) {
                    String strTemp = String.format("'%s'", strvalue.trim());

                    if (!strNearStaBuilder.toString().contains(strTemp)) {
                        strNearStaBuilder.append(strTemp).append(",");
                    }
                }
                strNearSta = strNearStaBuilder.toString();
                if (strNearSta.endsWith(",")) {
                    strNearSta = strNearSta.substring(0, strNearSta.length() - 1);
                }
            }

            boolean success = GetForecastBySta(listYB, list, listInsert, tfStatoNaSta, strNearSta, dateAnnounce, iNTimes, forecastType, iInterval);

            int insert = 0;
            if (listInsert != null && listInsert.size() > 0) {
                int delete = trafficMapper.delete(new LambdaQueryWrapper<Traffic>()
                        .eq(Traffic::getAnnouncedtime, Timestamp.valueOf(dateAnnounce))
                        .eq(Traffic::getForecasttype, iInterval));
                log.warn("删除了原来的：" + delete + "条数据！");
                for (Traffic traffic : listInsert) {
                    int i = trafficMapper.insert(traffic);
                    insert += i;
                }
                log.warn("插入数据： " + insert + "条数据");
                bSuccess = true;
            } else {
                log.warn("无预报数据！！！");
            }
        }
        return listInsert;
    }

    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    // 获取该时效的道路预报
    private boolean GetForecastBySta(List<TrafficFcst> listYB, List<TrafficWeatherSta> listSta, List<Traffic> insertListData, HashMap dicNearSta, String strNearStas, String dateAnnounce, int iNTimes, int forecastType, int iInterval) {
        boolean bResult = false;
        if (listYB != null && listYB.size() > 0) {
            LocalDateTime datetime1 = LocalDateTime.parse(dateAnnounce, formatter).minusHours(iNTimes);
            String formattedDatetime1 = datetime1.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            HashMap visible = getVisible(formattedDatetime1, dateAnnounce, strNearStas, forecastType, iNTimes);
            for (int t = 1; t * iInterval <= iNTimes; t++) {
                int iNtime = t * iInterval;
                List<TrafficFcst> listResult;
                if (forecastType == 1) {
                    listResult = listYB.stream()
                            .filter(tf -> tf.getNtimes() == iNtime && tf.getForecasttype() == forecastType)
                            .collect(Collectors.toList());
                } else {
                    int iNtimes0 = iNtime - forecastType;
                    listResult = listYB.stream()
                            .filter(tf -> (tf.getNtimes() == iNtime || tf.getNtimes() == iNtimes0) && tf.getForecasttype() == forecastType)
                            .collect(Collectors.toList());
                }
                if (listResult != null && listResult.size() > 0) {
                    String strStationID = "";
                    String strStation = "";
                    double dLon = 0.0;
                    double dLat = 0.0;
                    datetime1 = LocalDateTime.parse(dateAnnounce, formatter).plusHours(iNTimes);
                    List<TrafficFcst> list = new ArrayList<>();
                    for (int i = 0; i < listSta.size(); i++) {
                        //读取交通气象站点列表
                        strStationID = listSta.get(i).getStationid();
                        strStation = listSta.get(i).getStationname();
                        //加载经纬度
                        dLon = listSta.get(i).getLongitude();
                        dLat = listSta.get(i).getLatitude();
                        String finalStrStationID = strStationID;
                        list = listResult.stream()
                                .filter(tf -> tf.getStationid().trim().equals(finalStrStationID))
                                .collect(Collectors.toList());
                    }
                    String fx = "", fs = "", strWeather = "", Maxtemperture = "", Mintemperture = "", strForecast = "";
                    double drain = 0;
                    if (list.size() > 0) {
                        if (String.valueOf(list.get(0).getWindd()) != "") {
                            double fxTemp = list.get(0).getWindd();
                            fx = WeatherInfoUtil.getFX(fxTemp);
                        }
                        if (String.valueOf(list.get(0).getWeather1()) != "") {
                            strForecast = list.get(0).getWeather1();
                        }
                        if (String.valueOf(list.get(0).getWinds()) != "") {
                            fs = String.valueOf(list.get(0).getWinds());
                            fs = WeatherInfoUtil.GetWeatherFs(String.valueOf(WeatherInfoUtil.fengSuToFengJi(list.get(0).getWinds())));
                        }
                        if (String.valueOf(list.get(0).getMaxtemp()) != "") {
                            Maxtemperture = String.valueOf(list.get(0).getMaxtemp());
                        }
                        if (String.valueOf(list.get(0).getMintemp()) != "") {
                            Mintemperture = String.valueOf(list.get(0).getMintemp());
                        }
                        if (String.valueOf(list.get(0).getRain()) != null && !String.valueOf(list.get(0).getRain()).isEmpty()) {
                            drain = list.get(0).getRain();
                        }
                        //非1小时预报
                        if (list.size() > 1 && forecastType != 1) {
                            String maxtemp = "", mintemp = "";
                            if (list.get(1).getWeather1() != "") {
                                if (strForecast == "") {
                                    strForecast = list.get(1).getWeather1();
                                } else if (list.get(0).getWeather1() != list.get(1).getWeather1()) {
                                    strForecast = list.get(1).getWeather1() + "转" + list.get(0).getWeather1();
                                }
                            }
                            if (String.valueOf(list.get(1).getMaxtemp()) != "") {
                                maxtemp = String.valueOf(list.get(1).getMaxtemp());
                                if (Maxtemperture == "") {
                                    Maxtemperture = maxtemp;
                                } else if (Double.parseDouble(Maxtemperture) < Double.parseDouble(maxtemp)) {
                                    Maxtemperture = maxtemp;
                                }
                            }
                            if (String.valueOf(list.get(1).getMintemp()) != "") {
                                mintemp = String.valueOf(list.get(1).getMintemp());
                                if (Mintemperture == "") {
                                    Mintemperture = mintemp;
                                } else if (Double.parseDouble(Mintemperture) > Double.parseDouble(mintemp)) {
                                    Mintemperture = mintemp;
                                }
                            }
                            if (String.valueOf(list.get(1).getRain()) != null && !String.valueOf(list.get(1).getRain()).isEmpty()) {
                                drain += list.get(1).getRain();
                            }
                        }
                        //根据天气状况进行交通等级分类
                        int iForecastLevel = 1;
                        String MCXS = "";
                        strWeather = strForecast;
                        if (strWeather != "") {
                            //交通等级分类
                            iForecastLevel = WeatherInfoUtil.getForecastLevel(strWeather);
                            //得到摩擦系数
                            MCXS = WeatherInfoUtil.getForecastMCXS(strWeather);
                        }
                        //关联的能见度
                        String Vis = "";
                        DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH");
                        DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyy-MM-dd ");
                        String strViskey = dicNearSta.get(strStationID).toString();
                        if (forecastType == 1) {
                            strViskey = strViskey + "|" + datetime1.format(formatter1);
                        } else {
                            strViskey = strViskey + "|" + datetime1.format(formatter2);
                        }
                        if (dicNearSta != null && dicNearSta.containsKey(strStationID) && visible.containsKey(strViskey)) {
                            Vis = visible.get(strViskey).toString();
                        }

                        String LMMaxtemperture = "";
                        String LMMintemperture = "";
                        if (Maxtemperture != "") {
                            LMMaxtemperture = String.valueOf(Double.parseDouble(Maxtemperture) + 2);
                        }
                        if (Mintemperture != "") {
                            LMMintemperture = String.valueOf(Double.parseDouble(Mintemperture) + 2);
                        }

                        //道路等级对应的说明
                        String strExplain = WeatherInfoUtil.getLevelExplain(iForecastLevel);
                        int viss = 0;
                        if (Vis == null || Vis.isEmpty()) {
                            viss = 0;
                        } else {
                            viss = Integer.parseInt(Vis);
                        }

                        Traffic insertdata = new Traffic();
                        insertdata.setTrafficsid(strStationID);
                        insertdata.setStationname(strStation);
                        insertdata.setLongitude(dLon);
                        insertdata.setLatitude(dLat);
                        insertdata.setAnnouncedtime(Timestamp.valueOf(dateAnnounce));
                        insertdata.setForcastlevel(iForecastLevel);
                        insertdata.setViewlevel(viss);
                        insertdata.setExplain(strExplain);
                        insertdata.setWeather12(strWeather);
                        insertdata.setMaxtemp(Maxtemperture);
                        insertdata.setMintemp(Mintemperture);
                        insertdata.setWindd(fx);
                        insertdata.setWind(fs);
                        insertdata.setLmmaxtemp(LMMaxtemperture);
                        insertdata.setLmmintemp(LMMintemperture);
                        insertdata.setLmmcxs(MCXS);
                        insertdata.setNtimes(iNtime);
                        insertdata.setRain(String.valueOf(drain));
                        insertdata.setForecasttype(iInterval);
                        insertListData.add(insertdata);
                    }
                }
            }
        }
        return bResult;
    }

    // 得到前一天的实况数据，得到最小能见度
    private HashMap getVisible(String date1, String date2, String NearStationID, int forecastType, int iNtimes) {
        HashMap<String, String> Visible = new HashMap<>();
        List<MsgMediumSmallScale> list = new ArrayList<>();
        String NearStationIDs = NearStationID;
        if (!NearStationIDs.startsWith("'")) {
            NearStationIDs = "'" + NearStationIDs;
        }
        if (!NearStationIDs.endsWith("'")) {
            NearStationIDs = NearStationIDs + "'";
        }
        if (forecastType != 1) {
            list = msgMediumSmallScaleMapper.selectByTimePeriodAndNearStationIDs(Timestamp.valueOf(date1), Timestamp.valueOf(date2), NearStationIDs);
        } else {
            list = msgMediumSmallScaleMapper.selectByTimePeriodAndNearStationIDs1(Timestamp.valueOf(date1), Timestamp.valueOf(date2), NearStationIDs);
        }
        if (list != null && list.size() > 0) {
            for (MsgMediumSmallScale scale : list) {
                if (scale.getObservtime() == null) {
                    continue;
                }
                String strTime = String.valueOf(scale.getObservtime());
                LocalDateTime dtTime = LocalDateTime.parse(date2, formatter);
                if (forecastType == 1) {
                    long ihour = ChronoUnit.HOURS.between(LocalDateTime.parse(strTime, formatter), dtTime);
                    strTime = dtTime.plusHours(ihour).format(formatter);
                } else {
                    long iday = ChronoUnit.DAYS.between(LocalDateTime.parse(strTime, formatter), dtTime);
                    strTime = dtTime.plusDays(iday).format(formatter);
                }
                if (String.valueOf(scale.getMinvisibility()) == "-65535") {
                    Visible.put(scale.getStationid().trim() + "|" + strTime, "");
                } else {
                    Visible.put(scale.getStationid() + "|" + strTime, String.valueOf(scale.getMinvisibility()));
                }
            }
        }
        return Visible;
    }


    HashMap<String, StationInformation> TStations = new HashMap<String, StationInformation>();

    // 加载交通站点
    private void loadTrafficStation() {
        List<TrafficWeatherSta> list = trafficWeatherStaMapper.selectList(new LambdaQueryWrapper<TrafficWeatherSta>()
                .select(TrafficWeatherSta::getStationid, TrafficWeatherSta::getStationname, TrafficWeatherSta::getLongitude, TrafficWeatherSta::getLatitude));
        if (list != null && list.size() > 0) {
            for (TrafficWeatherSta sta : list) {
                StationInformation stationInformation = new StationInformation();
                stationInformation.setStationId(sta.getStationid());
                stationInformation.setStationName(sta.getStationname());
                stationInformation.setLongitude(sta.getLongitude());
                stationInformation.setLatitude(sta.getLatitude());
                TStations.put(sta.getStationid(), stationInformation);
            }
        } else {
            log.warn("交通站点数据未添加！数据可能为空！");
        }
    }

    HashMap<String, StationInformation> Stations = new HashMap<String, StationInformation>();
    List<String> li = new ArrayList<>();

    // 加载国家站
    public void loadStation() {
        List<SmallScaleStation> list = smallScaleStationMapper.getAllCountryStation();
        if (list != null && list.size() > 0) {
            for (SmallScaleStation sta : list) {
                StationInformation stationInformation = new StationInformation();
                stationInformation.setStationId(sta.getStationid());
                stationInformation.setStationName(sta.getStationname());
                stationInformation.setLongitude(sta.getLongitude());
                stationInformation.setLatitude(sta.getLatitude());
                Stations.put(sta.getStationid(), stationInformation);
                li.add(sta.getStationid());
            }
        } else {
            log.warn("国家站数据未添加！数据可能为空！");
        }
    }

    // 加载交通站点关联的国家站
    public HashMap<String, String> getNerNatSta() {
        HashMap<String, String> tfStatoNaSta = new HashMap<>();
        if (TStations != null && TStations.size() > 0) {
            HashMap<String, RelatedSites> dicRelSta = new HashMap<>();
            for (String strkey : TStations.keySet()) {
                if (TStations.get(strkey).getLongitude() != 0 && TStations.get(strkey).getLatitude() != 0) {
                    double iLon = TStations.get(strkey).getLongitude();
                    double iLat = TStations.get(strkey).getLatitude();
                    if (li == null || li.size() < 1) {
                        loadStation();
                    }
                    for (String stationid : li) {
                        if (!stationid.startsWith("5")) {
                            continue;
                        }
                        double iLon1 = (Stations.get(stationid).getLongitude());
                        double iLat1 = (Stations.get(stationid).getLatitude());
                        double distance = Math.pow((iLat1 - iLat), 2) + Math.pow((iLon1 - iLon), 2);
                        if (!dicRelSta.containsKey(strkey)) {
                            RelatedSites relSta = new RelatedSites();
                            relSta.setDistanceFc(distance);
                            relSta.setRelFcstSta(stationid);
                            dicRelSta.put(strkey, relSta);
                            tfStatoNaSta.put(strkey, stationid);
                        } else {
                            if (dicRelSta.get(strkey).getDistanceFc() > distance) {
                                dicRelSta.get(strkey).setDistanceFc(distance);
                                dicRelSta.get(strkey).setRelSkSta(stationid);
                                tfStatoNaSta.put(strkey, stationid);
                            }
                        }
                    }
                }
            }
        }
        return tfStatoNaSta;
    }

}






